/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package core.worker;

import java.awt.Desktop;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

import org.apache.log4j.Logger;

import core.descriptors.TaskDescriptor;
import core.descriptors.WorkerDescriptor;
import core.enums.Errors;
import core.enums.TaskTYPE;
import core.enums.WorkerStatus;
import core.interfaces.AbstractWorker;
import core.manager.TQueueManager;
import core.proxy.WorkerDiscoveryProxy;
import core.util.COREServiceRegistry;
import core.util.SockUtil;

/**
 * Oggetto che rappresenta l'esecutore del task sottomesso alla ui. Nella nostra implementazione abbiamo considerato il worker esegue il task in due differenti 
 * modi: se il task � di tipo Desktop, viene mandato esecuzione mediante la classa java.awt.Deskotop che invoca il programma di defualt del s.o. associato 
 * all'estensione del file; invece nel caso in cui il task sia tipo Executable o ScriptBash il task viene avviato mediante la classi ProcessBuilder e Process.
 * @see TaskTYPE
 * @see Desktop
 * @see ProcessBuilder
 * @see Process 
 * @author Vincenzo Merlo, Scuderi Giovanni Luca, Paolo Giuffrida
 */
public class Worker implements AbstractWorker {

    private final Logger logger = COREServiceRegistry.getLogger(Worker.class.toString());
    private WorkerDescriptor wDescriptor; //TODO: should be used
    private TQueueManager tQManager; //TODO: must be used
    private WorkerDiscoveryProxy workerDiscProxy;
    private WorkerQueueMonitor workerMonitor;
    private int ID = -1;

    /**
     * Costruttore di default, si occupa di instanziare:
     * - il WorkerDescriptor
     * - il WorkerDiscoveryProxy nel quale inserisce il proprio Address, inserendo l'indirro locale (localhost - 127.0.0.1) e la porta portMessagesSender, e il proprio
     * 		il WorkerDescriptor
     * - il TQueueManager
     * - il WorkerQueueMonitor
     * 
     * @see TQueueManager
     * @see WorkerDescriptor
     * @see WorkerDiscoveryProxy
     * @see WorkerQueueMonitor
     * 
     */
    public Worker() {
        //TODO: da implementare... dovrebbe inviare il suo workerdescriptor..
        Address addr = new Address();
        addr.setIP("127.0.0.1");
        addr.setPort(SockUtil.portMessagesSender);

        tQManager = new TQueueManager(addr);
        tQManager.setAddr(addr);
        workerDiscProxy = new WorkerDiscoveryProxy();
        wDescriptor = new WorkerDescriptor();
        wDescriptor.setAddr(addr);
        wDescriptor.setStatus(WorkerStatus.Free);
        this.ID = workerDiscProxy.addWorker(wDescriptor);

        workerMonitor = new WorkerQueueMonitor(this);
        this.run();
    }

    /** 
     * esegue il task in due differenti 
     * 	modi: se il task � di tipo Desktop, viene mandato esecuzione mediante la classa java.awt.Deskotop che invoca il programma di defualt del s.o. associato 
     * 	all'estensione del file; invece nel caso in cui il task sia tipo Executable o ScriptBash il task viene avviato mediante la classi ProcessBuilder e Process e restituisce
     * l'output del programma.
     * @return int > 0 se tutto � andato a buon fine, < 0 in caso di errore
     */
    public int exec(TaskDescriptor td) {
        
        logger.debug("exec: " + td);

        String s = null;
        File file = null;
        Process p = null;
        ProcessBuilder pb = new ProcessBuilder();

        /*if ('/' == (File.separatorChar)) {
        pb.command(SockUtil.dirDestination + File.separatorChar + td.getCommand());
        logger.debug("linux: " + SockUtil.dirDestination + File.separatorChar + td.getCommand());
        //file = new File(SockUtil.dirDestination + File.separatorChar + td.getCommand());
        } else {
        logger.debug("winzoz: " + SockUtil.dirDestination + File.separatorChar + td.getCommand());
        pb.command(SockUtil.dirDestination + td.getCommand());
        logger.debug(SockUtil.dirDestination + td.getCommand());
        //file = new File(SockUtil.dirDestination + td.getCommand());
        }*/
        pb = pb.directory(new File(SockUtil.dirDestination));
        pb = pb.command(td.getCommand().split(" "));


        switch (td.getType()) {

            case Desktop: //se + di tipo Desktop demando l'esecuzione e la ricerca del comando giusto al sistema operativo
                String fileName = new File(td.getLink()).getName();
                try {
                    if ('/' == (File.separatorChar)) {
                        //pb = pb.command(SockUtil.dirDestination + File.separatorChar + td.getCommand());
                        if (fileName.contains("\\")) {
                            String[] filen = fileName.split("\\\\");
                            fileName = filen[filen.length - 1];
                        }

                        file = new File(SockUtil.dirDestination + File.separatorChar + fileName);
                        logger.debug("linux: " + SockUtil.dirDestination + File.separatorChar + fileName);
                    } else {
                        //pb = pb.command(SockUtil.dirDestination + td.getCommand());
                        logger.debug(SockUtil.dirDestination + td.getCommand());
                        file = new File(SockUtil.dirDestination + fileName);
                        logger.debug("winzoz: " + SockUtil.dirDestination + fileName);
                    }
                    //logger.debug("Desktop file : " + file. getCanonicalFile());
                    Desktop.getDesktop().open(file);
                    COREServiceRegistry.getDiscovery().updateWorker(getID(), wDescriptor);
                    return 1;
                } catch (IOException e) {
                    return Errors.IOException.code();
                } catch (Exception e) {
					return Errors.Exception.code();
				}


            case Executable: // se è di tipo Executable eseguo il link inviato
                pb.redirectErrorStream(true);

                try {
                    logger.debug("Comando da eseguire " + pb.command() + " Directory da cui eseguire  " + pb.directory());
                    logger.debug("Comando da eseguire " + pb.command() + " Directory Environment  " + pb.environment().get("Path"));
                    p = pb.start();

                    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
                    BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
                    // read the output from the command
                    while ((s = stdInput.readLine()) != null) {
                        System.out.println(s);
                    }

                    // read any errors from the attempted command
                    while ((s = stdError.readLine()) != null) {
                        System.out.println(s);
                    }

                    stdInput.close();
                    stdError.close();
                    if (this.getTQueueManager().gettQueue().isEmpty()) {
                        this.wDescriptor.setStatus(WorkerStatus.Free);
                    }

                    COREServiceRegistry.getDiscovery().updateWorker(getID(), wDescriptor);

                    return 1;

                } catch (IOException e) {
                    logger.debug(this.getClass() + " " + e.getClass().getCanonicalName() + " " + e.getMessage());
                    return Errors.IOException.code();
                } catch (Exception e) {
                    logger.debug(this.getClass() + " " + e.getClass().getCanonicalName() + " " + e.getMessage());
                    return Errors.Exception.code();
                }



            case ScriptBash:
                pb.redirectErrorStream(true);


                try {
                    p = pb.start();
                    BufferedReader stdInput = new BufferedReader(new InputStreamReader(p.getInputStream()));
                    BufferedReader stdError = new BufferedReader(new InputStreamReader(p.getErrorStream()));
                    // read the output from the command
                    while ((s = stdInput.readLine()) != null) {
                        System.out.println(s);
                    }

                    // read any errors from the attempted command
                    while ((s = stdError.readLine()) != null) {
                        System.out.println(s);
                    }

                    stdInput.close();
                    stdError.close();
                    if (this.getTQueueManager().gettQueue().isEmpty()) {
                        this.wDescriptor.setStatus(WorkerStatus.Free);
                    }

                    COREServiceRegistry.getDiscovery().updateWorker(getID(), wDescriptor);

                    return 1;

                } catch (IOException e) {
                    logger.debug(this.getClass() + " " + e.getClass().getCanonicalName() + " " + e.getMessage());
                    return Errors.IOException.code();
                } catch (Exception e) {
                    logger.debug(this.getClass() + " " + e.getClass().getCanonicalName() + " " + e.getMessage());
                    return Errors.Exception.code();
                }


            default:
                break;
        }

        return Errors.Otherwise.code();

    }

    /**
     *
     * @return
     */
    @Override
    public String toString() {
        StringBuilder sb = new StringBuilder(Worker.class.getSimpleName());
        sb.append("\n\tWorkerDescriptor: ").append(this.wDescriptor).append("\n\tTQueueManager: ").append(this.getTQueueManager()).append("\n\tWorkerDiscoveryProxy: ").append(this.workerDiscProxy);

        return sb.toString();
    }

    /**
     * @return the tQueueManag
     */
    public TQueueManager getTQueueManager() {
        return tQManager;
    }

    /**
     * @param tQueueManag the tQueueManag to set
     */
    public void setTQueueManager(TQueueManager tQueueManag) {
        this.tQManager = tQueueManag;
    }

    /**
     * Istanzia il WorkerQueueMonitor mediantre un ExecutorService
     */
    private void run() {
        logger.debug("run!!!!!");
        ExecutorService threadExecutor = Executors.newSingleThreadExecutor();
        workerMonitor = new WorkerQueueMonitor(this);
        threadExecutor.execute(workerMonitor);
    }

    /**
     * @return the ID
     */
    public int getID() {
        return ID;
    }

    /**
     * @param ID the ID to set
     */
    public void setID(int ID) {
        this.ID = ID;
    }
}
